
class FieldComposite < FormField
  #Handler.enable self
  attr_accessor :app, :page, :fields, :success

  def initialize(html_attrs = {})
    init_element Element.new(:div, html_attrs)
    @fields = []
  end

  def << something
    super
    if something.methods.include? :form=
      something.form = self.form
    end
    (@fields ||= []) << something
    something
  end

  def remove something
    super
    #element.remove_child something
    #something.form = nil
    @fields = @fields - [something]
    something
  end

  def value(name)
    field(name).value
  end

  def label name, label
    e = Element.new(:label, :for => field(name).id)
    e.append_child label
    e
  end

  def render name
    field(name).to_s
  end

  def field name
    name = name.to_s
    field = @fields.select { |child|
      child.name.to_s == name
    }[0]
    if !field
      names = @fields.collect { |f| f.name }
      raise("No field '#{name}' here. Available: #{names.inspect}.")
    end
    field
  end

  def fill data
    return if data.nil?
    data = data.stringify_keys
    if self.name
      data = data[@name]
    end
    return if data.nil?

    @fields.each { |child|
      if child.respond_to? :fill
        if child.class.ancestors.include? FieldComposite
          child.fill data
        elsif !data[child.name].nil?
          child.fill data[child.name]
        end
      end
    }
  end

  def recieve data
    return if !data
    data = data.stringify_keys

    if self.name
      data = data[@name]
    end
    return if !data

    @fields.each { |child|
      if child.respond_to? :recieve
        if child.class.ancestors.include? FieldComposite
          child.recieve data
        else
          if child.name #&& !data[child.name].nil?
            child.recieve data[child.name]
          end
        end
      end
    }
  end

  def post
    dump
  end

  def dump
    dump = {}
    @fields.each { |f|
      if f.class.ancestors.include?(FieldComposite)
        dump.merge!(f.dump)
      elsif f.respond_to?('has_value?') && f.has_value?
        if f.name.nil?
          raise "The field #{f} must not be nil."
        end
        dump[f.name] = f.value
      end
    }
    if self.name
      dump = {@name => dump}
    end
    dump
  end

  def value
    dump
  end

  def has_value?
    true
  end

  def handle data
    if @success != nil
      raise 'Already handled!'
    end
    recieve(data)
    if valid?
      on_success
      @success = true
    else
      on_fail
      @success = false
    end
  end

  def on_success
    @fields.each { |f|
      if f.respond_to? :on_success
        f.on_success
      end
    }
  end

  def valid?
    true
  end

  def visible?
    true
  end

  def on_fail
    raise 'failed'
  end






  class Test < FwTest

    def test_form_composition
      form = FieldComposite.new
      input = InputText.new(:name => 'login')
      form<< input
      assert form.fields.include? input
    end

    def test_field_fetch
      form = FieldComposite.new
      input = InputText.new(:name => 'login')
      form<< input
      assert_equal input, form.field(:login)
    end

    def test_field_raise_when_no_such_field
      assert_raise(RuntimeError) { Form.new.field(:no_such_field) }
    end

    def test_render_field
      form = FieldComposite.new
      input = InputText.new(:name => 'login')
      form<< input
      assert_equal input.to_s, form.render(:login)
    end

    def test_dump_makes_hash_out_of_names_and_dumps_of_fields
      form = FieldComposite.new
      form<< InputText.new(:name => 'color', :value => 'blue')
      assert_equal('blue', form.dump['color'])
    end

    class MyTestForm < FieldComposite
      def on_success; end
    end

    def test_raise_when_executing_handle_twice_on_one_form
      form = MyTestForm.new
      form.handle({})
      assert_raise(RuntimeError) {
        form.handle({})
      }
    end

    def test_remove_field
      form = FieldComposite.new
      input = InputText.new(:name => 'login')
      form<< input
      form.remove input
      assert !form.fields.include?(input)
    end

    def test_dump_doesnt_override_values_that_are_not_present_in_hash
      form = FieldComposite.new
      first_name = InputText.new(:name => 'firstname', :value => 'john')
      form<< first_name
      surname = InputText.new(:name => 'surname', :value => 'malkovich')
      form<< surname
      assert_equal('john', first_name.value)
      form.fill({'surname' => 'lennon'})
      assert_equal('john', first_name.value)
    end


    # name prefixing

#    def test_fields_use_name_prefixes_from_parents
#      c = FieldComposite.new
#      input = InputText.new(:name => 'name', :value => 'john')
#      c << input
#      c.name = "buddy"
#      assert_match(/name="buddy\[name\]"/, input.to_s)
#    end

    def test_dump_prefixes_fields_by_its_name
      c = FieldComposite.new
      c<< InputText.new(:name => 'name', :value => 'john')
      c.name = "buddy"
      assert_equal({'buddy' => {'name' => 'john'}}, c.dump)
    end

    def test_recieve_prefixes_fields_by_its_name
      c = FieldComposite.new
      name = c<< InputText.new(:name => 'name', :value => 'john')
      c.name = "buddy"
      c.recieve 'buddy' => {'name' => 'george'}
      assert_equal 'george', name.value
    end


    def test_fill_puts_data_into_fields
      c = FieldComposite.new
      name = c<< InputText.new(:name => 'name', :value => 'john')
      c.fill 'name' => 'george'
      assert_equal 'george', name.value
    end

    def test_fill_puts_data_into_fields_using_stringified_keys
      c = FieldComposite.new
      name = c<< InputText.new(:name => 'name', :value => 'john')
      c.fill :name => 'george'
      assert_equal 'george', name.value
    end

    def test_fill_prefixes_fields_by_its_name
      c = FieldComposite.new
      name = c<< InputText.new(:name => 'name', :value => 'john')
      c.name = "buddy"
      c.fill 'buddy' => {'name' => 'george'}
      assert_equal 'george', name.value
    end

  end


  class CompositionTest < FwTest
    def test_recieve_values
      parent = FieldComposite.new
      child = FieldComposite.new
      parent<< child
      child<< color = InputText.new(:name => 'color')
      parent.recieve({'color' => 'blue'})
      assert_equal 'blue', color.value
    end

    def test_recieve_values_stringifies_keys
      parent = FieldComposite.new
      child = FieldComposite.new
      parent<< child
      child<< color = InputText.new(:name => 'color')
      parent.recieve({:color => 'blue'})
      assert_equal 'blue', color.value
    end

    def test_recieve_dump_values
      parent = FieldComposite.new
      child = FieldComposite.new
      parent<< child
      child<< InputText.new(:name => 'color')
      parent.recieve({'color' => 'blue'})
      assert_equal({'color' => 'blue'}, parent.dump)
    end


    def test_recieve_dump_values_with_form
      parent = Form.new
      child = FieldComposite.new
      parent<< child
      child<< InputText.new(:name => 'color')
      parent.recieve({'color' => 'blue'})
      assert_equal({'color' => 'blue'}, parent.dump)
    end

    class ChildWithDefinedOnSuccess < FieldComposite
      attr_accessor :called
      def initialize
        @called = false
      end
      def on_success
        @called = true
      end
    end

    def test_on_success_calles_on_success_on_children
      parent = FieldComposite.new
      child = ChildWithDefinedOnSuccess.new
      parent << child
      assert !child.called
      parent.on_success
      assert child.called
    end

    def test_on_success_calles_on_success_on_children_from_form
      parent = Form.new
      child = ChildWithDefinedOnSuccess.new
      parent << child
      assert !child.called
      parent.on_success
      assert child.called
    end

    def test_dump_two_nested_fieldcomposite
      parent = FieldComposite.new
      child = FieldComposite.new
      parent << child
      child << InputText.new(:name => 'color', :value => 'blue')

      assert_equal({'color' => 'blue'}, child.dump)
      assert_equal({'color' => 'blue'}, parent.dump)
    end

    def test_dump_three_nested_fieldcomposite
      parent = FieldComposite.new
      child = FieldComposite.new
      childchild = FieldComposite.new
      parent << child
      child << childchild
      childchild << InputText.new(:name => 'color', :value => 'blue')

      assert_equal({'color' => 'blue'}, childchild.dump)
      assert_equal({'color' => 'blue'}, child.dump)
      assert_equal({'color' => 'blue'}, parent.dump)
    end

    def test_dump_fieldcomposite_with_form
      parent = Form.new
      child = FieldComposite.new
      parent << child
      child << InputText.new(:name => 'color', :value => 'blue')

      assert_equal({'color' => 'blue'}, child.dump)
      assert_equal({'color' => 'blue'}, parent.dump)
    end
  end


end

